/*******************************
*  Hari Dasu Algorithm Implementation Header
*
*  Authors: Mark Anderson, Jeremy Goldin, Nathan Folkner
*
*  Contains function prototypes, typedefs, and defines
*
********************************/

#ifndef HARIDASU_H
#define HARIDASU_H
#define NUM_FF 5000
#define NUM_BRAM 50
#define NUM_LUT 5000
#define NUM_DSP 50

#define MAX_BB 4

#define FDS_DEBUG
#include <vector>
#include <iostream>
#include <limits>
#include <math.h>
#include <list>
#include <stdio.h>
#include <stdlib.h>
#include <map>
#include <string.h>
#include <fstream>
#include <cmath>

#include "resourceTypes.h" 

#define LATENCY 500		//make this the maximum of whatever latency we will be dealing with for the DG arrays

using namespace std;

/* Available primitives */
struct A
{
	//NOTE: NUM_PRIM will always be four.
	int prims[NUM_PRIM];

	/* default constructor puts zero for every primitive*/
	A()
	{
		for(int i = 0; i < NUM_PRIM; i++)
		{
			prims[i] = 0;
		}
	}
	/* the ability to set two sets equal */
	A& operator=(const A &rhs)
	{
		//TODO: Check for self assignment
		for(int i=0; i<NUM_PRIM; i++)
		{
			this->prims[i] = rhs.prims[i];
		}
		return *this;
    }

	/* the ability to take the difference of two res sets */
	A& operator-=(const A &rhs)
	{
		for(int i=0; i<NUM_PRIM; i++)
		{
			this->prims[i] = this->prims[i]-rhs.prims[i];
		}
		return *this;
    }

	bool lessThanZero()
	{
		for(int i = 0; i< NUM_PRIM; i++)
		{
			if(prims[i]<0) return true;
		}
		return false;
	}
};

/*******************
*  The resource type with its properties,
*  i.e. how many implementations,
*       array of implementations of type A
*
*  we may consider moving this to its own file
********************/
struct R
{
	int myType;
	int numImp;
	A* resImp;
	// TODO: Each resource type will need us to explicitly define
	//       the different implementations.  For now, garbage is in there.
	/* default constructor */
	R()
	{
		myType = resTypeIter;
		resTypeIter = (resTypeIter+1 == NUM_RES) ? 0 : resTypeIter+1;  //ugly fix for an ugly problem
		// Brace yourself...
		/**********************************
		* MODIFY THESE TO WHATEVER YOU WANT
		***********************************/

		switch(myType)
		{
			case IADD_TYPE:
				numImp=1;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 32;
				resImp[0].prims[LUT] = 32;
				resImp[0].prims[DSP] = 0;   // Alternatively, we could make a function in the A struct to add these all at once
			break;
			case FADD_TYPE:
				numImp=2;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 579;
				resImp[0].prims[LUT] = 562;
				resImp[0].prims[DSP] = 0; 

				resImp[1].prims[BRAM] = 0;
				resImp[1].prims[FF] = 476;
				resImp[1].prims[LUT] = 340;
				resImp[1].prims[DSP] = 4;
			break;
			case IMULT_TYPE: 
				numImp=3;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 576;
				resImp[0].prims[LUT] = 512;
				resImp[0].prims[DSP] = 0; 

				resImp[1].prims[BRAM] = 0;
				resImp[1].prims[FF] = 385;
				resImp[1].prims[LUT] = 777;
				resImp[1].prims[DSP] = 1;

				resImp[2].prims[BRAM] = 0;
				resImp[2].prims[FF] = 32;
				resImp[2].prims[LUT] = 17;
				resImp[2].prims[DSP] = 3;
			break;
			case FMULT_TYPE: 
				numImp=3;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 689;
				resImp[0].prims[LUT] = 608;
				resImp[0].prims[DSP] = 0; 

				resImp[1].prims[BRAM] = 0;
				resImp[1].prims[FF] = 188;
				resImp[1].prims[LUT] = 124;
				resImp[1].prims[DSP] = 4;

				resImp[2].prims[BRAM] = 0;
				resImp[2].prims[FF] = 171;
				resImp[2].prims[LUT] = 96;
				resImp[2].prims[DSP] = 5;
			break;
			case ISUB_TYPE:
				numImp=1;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 32;
				resImp[0].prims[LUT] = 32;
				resImp[0].prims[DSP] = 0; 
			break;
			case FSUB_TYPE: 
				numImp=2;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 579;
				resImp[0].prims[LUT] = 562;
				resImp[0].prims[DSP] = 0; 

				resImp[1].prims[BRAM] = 0;
				resImp[1].prims[FF] = 476;
				resImp[1].prims[LUT] = 340;
				resImp[1].prims[DSP] = 4;
			break;			
			case RIGHT_SHIFT_TYPE: 
				numImp=1;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 0;
				resImp[0].prims[LUT] = 32;
				resImp[0].prims[DSP] = 0; 
			break;			
			case LEFT_SHIFT_TYPE:
				numImp=1;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 32;
				resImp[0].prims[LUT] = 136;
				resImp[0].prims[DSP] = 0; 
			break;			
			case LOAD_TYPE: 
				numImp=1;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 1;		//should be 1 BRAM for every 2 Loads
				resImp[0].prims[FF] = 0;
				resImp[0].prims[LUT] = 0;
				resImp[0].prims[DSP] = 0; 
			break;			
			case STORE_TYPE: 
				numImp=1;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 1;		//should be 1 BRAM for every 2 store
				resImp[0].prims[FF] = 0;
				resImp[0].prims[LUT] = 0;
				resImp[0].prims[DSP] = 0; 
			break;			
			case IDIV_TYPE:
				numImp=1;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 3336;
				resImp[0].prims[LUT] = 1220;
				resImp[0].prims[DSP] = 0; 
			break;			
			case FDIV_TYPE:
				numImp=1;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 1354;
				resImp[0].prims[LUT] = 806;
				resImp[0].prims[DSP] = 0; 
			break;			
			case CMP_TYPE:
				numImp=1;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 32;
				resImp[0].prims[LUT] = 64;
				resImp[0].prims[DSP] = 0; 
			break;
			case IABS_TYPE:
				numImp=1;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 32;
				resImp[0].prims[LUT] = 62;
				resImp[0].prims[DSP] = 0; 
			break;
			case FABS_TYPE:
				numImp=1;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 31;
				resImp[0].prims[LUT] = 0;
				resImp[0].prims[DSP] = 0; 
			break;
			/*case ISQR_TYPE:
				numImp=1;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 1191;
				resImp[0].prims[LUT] = 1218;
				resImp[0].prims[DSP] = 0; 
			break;*/
			case FSQR_TYPE:
				numImp=1;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 807;
				resImp[0].prims[LUT] = 509;
				resImp[0].prims[DSP] = 0; 
			break;
			case IAND_TYPE:
				numImp=1;
				resImp = new A[numImp];
				resImp[0].prims[BRAM] = 0;
				resImp[0].prims[FF] = 32;
				resImp[0].prims[LUT] = 32;
				resImp[0].prims[DSP] = 0;
			break;
		} //end switch
	} //end constructor
};

/* Resource set */
struct S
{
	int resCount[NUM_RES];      //the number of each resource in resource set 'S'
	int resImpl[NUM_RES][MAX_IMPL];
	int latency;                // the latency/CP of the resource set

	/* default constructor */
	S()
	{
		for(int i= 0; i < NUM_RES; i++)
		{
			resCount[i] = 0;    // init the count of each res to 0
			for(int j=0; j<MAX_IMPL; j++)
			{
				resImpl[i][j]=0;
			}
		}
	}

	bool minSet()
	{
		int count = 0;
		for(int i =0; i< NUM_RES; i++)
		{
			if(resCount[i]>2)
				return false;
			else if( resCount[i] == 2)
				count++;
			if(count > 1)
				return false;
		}
		return true;
	}

	/* overload == operator for comparison 
	*  considered equal if both sets require the same number of resources*/
	bool operator==(const S &other) const
	{
		for(int i =0; i < NUM_RES; i++)
		 {
			if((*this).resCount[i] != other.resCount[i])
				return false;
		 }
		 return true;
	 }

	/* does right hand require more of any resource than the left hand? */
	bool operator>(const S &other) const
	{
		for(int i =0; i< NUM_RES; i++)
		{
			if((*this).resCount[i] > other.resCount[i])
				return true;
		}
		return false;
	}

	/* the ability to take the difference of two res sets */
	S& operator-=(const S &rhs)
	{
		for(int i=0; i<NUM_RES; i++)
		{
			this->resCount[i] = (this->resCount[i]-rhs.resCount[i] < 0) ? 0 : this->resCount[i]-rhs.resCount[i];
		}
		return *this;
    }
	S& operator+=(const S &rhs)
	{
		for(int i=0; i<NUM_RES; i++)
		{
			this->resCount[i] = this->resCount[i] + rhs.resCount[i];
		}
		return *this;
    }
    const S operator-(const S &other) const {
		return S(*this) -= other;
    }
	S& operator+(const S &rhs)
	{
		for(int i=0; i<NUM_RES; i++)
		{
			this->resCount[i] = (this->resCount[i] > rhs.resCount[i]) ? this->resCount[i] : rhs.resCount[i];
			for(int j = 0; j < MAX_IMPL; j++)
			{
				this->resImpl[i][j] =  this->resImpl[i][j] + rhs.resImpl[i][j];
			}
		}
		return *this;
    }

};


/* SWAT describes an architecture */
struct SWAT
{
	S setRes;
	float weight;
	A availPrims;
	int time;
	int name;
	int BB_Latency[MAX_BB];
	/* default constructor */
	SWAT()
	{
		for(int i = 0; i < MAX_BB; i++)
		{
			BB_Latency[i] = -1;

		}
		weight = 0;
		time = 0;
	}
};

/* DFG/BB.  The collection of resources and their connections that make up a portion of the CDFG */

typedef struct VERTEX
{
	int vertexNum;			//who I am
	int scheduled;			//whether or not I'm scheduled (1 for true, 0 for false)
	int timestep;			//when I was scheduled
	int timeFrameMin;		
	int timeFrameMax;
	int predecesors[2];		//who my 2 predecesors are
	int sucessors[2];
	int type;				//what type
	int weight;				//distance from me to farthest end vertex
	int length;				//distance from me to closest entry vertex
	int ready;				//am I ready?		
	float prob;

	VERTEX()
	{
		scheduled = 0;
		ready = 0;
		timestep = 0;
		timeFrameMin = 0;
		timeFrameMax = 0;
		prob = 0;
	}

}	vertex;

struct BB
{
	int name;				//BB # (0, 1, 2, etc)
	int minLatency; 
	int curLatency;          //the latency of the BB we're currently looking at.
	float prob;
	int numIt;
	list<vertex *> allVertices;
};
/*****end type definitions ******/

/**** begin function declarations ****/

/*************************
* WSDP()
*
* Relative Area Cost
*
* Inputs:
*        A kSubI       - the primitives used by the implmentation
*        A aSubI       - available primitives
*
* Outputs: 
*        float weight
*************************/
float WSDP(A kSubI, A aSubI);

/**************************
* calcTmin()
*
* Inputs: a CDFG.... a collection of BBs
*
* Outputs: int result
**************************/
int calcTmin(vector<BB>);

/*************************
* calcT()
* 
* Inputs: 
*         Tin - the base T
*         BB  - the BB we are modifying
* Outputs: 
*         an integer with the adjusted T
****************************/
int calcT(int, BB);
int estimateLUTMUX(int n, int dataWidth);
/*************************
*  Evaluate()
*
* Inputs:
*         (type?) DFG        - the data flow graph being evaluated
*         vector <S> resSets - the resource sets of DFG
*         SWAT curSolution   - the current solution
* Outputs:
*         vector <SWAT> X - set of partial solutions
*
*************************/
//TODO: move implementation out of header file
int printSWATS(vector<SWAT>* in);

#define delay_cycles 8
void evaluate(int latency, vector<S> resSets, SWAT inSWAT);

vector<BB> CDFGMaker(vector<BB> CDFG, char* CDFGname);
int printSWAT(SWAT in);
int FDS(list<vertex*> *passedVertices, int passedLatency, S *resourceSet, FILE * schedule, int param);	//takes in a given latency and the DFG in the list of vertex format and returns a set of resources, struct S
void printVertices();
void printDG();
void updateASAP_ALAP(vertex *);
float SPF(float, vertex * it , int i);
void ASAP(vertex *node, int);
void ALAP(vertex *node, int latency);
int ASAPinit(vertex *node, int);
int ALAPinit(vertex *node, int latency);
int ASAP_BBinit(BB bb1, vertex *node, int latency);
void configureASAP_ALAP(int, int);
void executeHariDasu(char * cdfgFileName, A initialAvailPrims);
void calcDG(int);
void mapping(SWAT* inSWAT, float* Wmux, float* Wreg);
bool findBusMatches(){return false;};
const int numDReg = 4;
//vars
FILE * schedule;
vector<BB> CDFG; //the CDFG
R resTypes[NUM_RES];        //the resource types with their implementations GLOBAL CONSTANT
list<vertex*> allVertices;
map<float, int> totalForce;
float DGIMULT[LATENCY+1];
float DGFMULT[LATENCY+1];
float DGIADD[LATENCY+1];
float DGFADD[LATENCY+1];
float DGISUB[LATENCY+1];
float DGFSUB[LATENCY+1];
float DGSHIFTR[LATENCY+1];
float DGSHIFTL[LATENCY+1];
float DGLOAD[LATENCY+1];
float DGSTORE[LATENCY+1];
float DGIDIV[LATENCY+1];
float DGFDIV[LATENCY+1];
float DGCMP[LATENCY+1];
float DGFABS[LATENCY+1];
float DGFSQR[LATENCY+1];
float DGIABS[LATENCY+1];
float DGIAND[LATENCY+1];
int delayRegCount(vertex *node, BB DFG);


#endif
